/**************************************************************************** 
 * Meta，一个轻量级的静态分区Hypervisor 
 *
 * 版权(c) , 2022-
 *
 * 作者:
 *      Tupelo Shen <shenwanjiang2013@163.com>
 *
 * Meta是一个自由软件，你可以在遵循GNU GPLv2协议的条件下，重新修改并发布它。
 *
 ***************************************************************************/
; #include <arch/meta.h>
; #include <arch/sysregs.h>
#include <asm_defs.h>

/**
 * ARMv8架构规范规定，Aarch64状态下，每个向量占用128字节，最多包含32个指令。
 * 按照0x800（2K大小）对齐。
 *
 * 初始化时，将该向量表的基地址写入VBAR_EL2寄存器即可。
 */

/* 每个向量的大小128字节 */
#define ENTRY_SIZE   (0x80)

.text
/**
 * 保存hypervisor使用的通用寄存器
 *
 * 1. 保存通用寄存器
 * 
 */
.macro SAVE_HYP_GPRS

    sub sp, sp, 32*8

    stp x0, x1,   [sp, #(8*0)]
    stp x2, x3,   [sp, #(8*2)]
    stp x4, x5,   [sp, #(8*4)]
    stp x6, x7,   [sp, #(8*6)]
    stp x8, x9,   [sp, #(8*8)]
    stp x10, x11, [sp, #(8*10)]
    stp x12, x13, [sp, #(8*12)]
    stp x14, x15, [sp, #(8*14)]
    stp x16, x17, [sp, #(8*16)]
    stp x18, x19, [sp, #(8*18)]
    stp x20, x21, [sp, #(8*20)]
    stp x22, x23, [sp, #(8*22)]
    stp x24, x25, [sp, #(8*24)]
    stp x26, x27, [sp, #(8*26)]
    stp x28, x29, [sp, #(8*28)]

    mov x29, sp 
    add x29, x29, 32*8
    stp x30, x29, [sp, #(8*30)]

.endm

/**
 * 退出虚拟机
 *
 * 1. 保存通用寄存器到堆栈中
 * 2. 保存异常链接寄存器
 * 3. 保存程序状态寄存器
 * 4. 保存hypervisor堆栈和寄存器到`cpu`数据结构中
 * 
 */
.macro VM_EXIT
    sub sp, sp, #(VCPU_REGS_SIZE)

    stp x0, x1,   [sp, #(8*0)]
    stp x2, x3,   [sp, #(8*2)]
    stp x4, x5,   [sp, #(8*4)]
    stp x6, x7,   [sp, #(8*6)]
    stp x8, x9,   [sp, #(8*8)]
    stp x10, x11, [sp, #(8*10)]
    stp x12, x13, [sp, #(8*12)]
    stp x14, x15, [sp, #(8*14)]
    stp x16, x17, [sp, #(8*16)]
    stp x18, x19, [sp, #(8*18)]
    stp x20, x21, [sp, #(8*20)]
    stp x22, x23, [sp, #(8*22)]
    stp x24, x25, [sp, #(8*24)]
    stp x26, x27, [sp, #(8*26)]
    stp x28, x29, [sp, #(8*28)]
    str x30,      [sp, #(8*30)]

    mrs x0, ELR_EL2
    mrs x1, SPSR_EL2
    stp x0, x1,   [sp, #(8*31)]

    ldr x0, =cpu
    ldr x0, [x0, #CPU_VCPU_OFF]
    mov x1, sp
    str x1, [x0, #VCPU_REGS_OFF]
.endm

/**
 * 加载虚拟机
 *
 * 1. 找到虚拟机的堆栈指针
 * 2. 读取异常链接寄存器
 * 3. 读取程序状态寄存器
 * 4. 加载通用寄存器
 * 5. 加载堆栈
 */
.global vcpu_arch_entry
vcpu_arch_entry:
    ldr x0, =cpu
    ldr x0, [x0, #CPU_VCPU_OFF]
    ldr x0, [x0, #VCPU_REGS_OFF]
    mov sp, x0

    ldp x0, x1, [sp, #(8*31)]
    msr ELR_EL2, x0
    msr SPSR_EL2, x1

    ldp x0, x1,   [sp, #(8*0)]
    ldp x2, x3,   [sp, #(8*2)]
    ldp x4, x5,   [sp, #(8*4)]
    ldp x6, x7,   [sp, #(8*6)]
    ldp x8, x9,   [sp, #(8*8)]
    ldp x10, x11, [sp, #(8*10)]
    ldp x12, x13, [sp, #(8*12)]
    ldp x14, x15, [sp, #(8*14)]
    ldp x16, x17, [sp, #(8*16)]
    ldp x18, x19, [sp, #(8*18)]
    ldp x20, x21, [sp, #(8*20)]
    ldp x22, x23, [sp, #(8*22)]
    ldp x24, x25, [sp, #(8*24)]
    ldp x26, x27, [sp, #(8*26)]
    ldp x28, x29, [sp, #(8*28)]
    ldr x30,      [sp, #(8*30)]

    add sp, sp, #(VCPU_REGS_SIZE)
    
    eret
    b   .

/*************************************************************************
 *****      以下是异常向量的入口（即异常向量表）                     *****
 *************************************************************************/
.balign 0x800
.global _hyp_vector_table   
_hyp_vector_table:

/**
 * EL2 with SP0
 *
 * 目前，在虚拟化层不处理Guest OS的内容，所以该向量表暂时没有处理。
 */  
.balign ENTRY_SIZE
curr_el_sp0_sync:        
    b   .
.balign ENTRY_SIZE
curr_el_sp0_irq:  
    b   .
.balign ENTRY_SIZE
curr_el_sp0_fiq:         
    b   .
.balign ENTRY_SIZE
curr_el_sp0_serror:      
    b   .
          
/**
 * EL2 with SPx 
 *
 * 在虚拟化层（使用SP2）产生异常时，进入abort异常处理程序。
 * 在此之前，需要先保存hypervisor使用的通用寄存器
 */  
.balign ENTRY_SIZE  
curr_el_spx_sync:
    SAVE_HYP_GPRS
    mov x0, sp
    bl  internal_abort_handler        
    b   .
.balign ENTRY_SIZE
curr_el_spx_irq:         
    b   .
.balign ENTRY_SIZE
curr_el_spx_fiq:         
    b   .
.balign ENTRY_SIZE
curr_el_spx_serror:
    SAVE_HYP_GPRS
    mov x0, sp
    bl  internal_abort_handler       
    b   .         

/* 
 * Lower EL using AArch64（通俗理解就是Guest OS运行在AArch64状态）
 *
 * 当运行在低异常级别时，产生异常后，就会进入该异常向量表。之后：
 *
 * 1. 退出虚拟机
 * 2. 进入异常处理程序（包括异常和中断）
 * 3. 重新进入虚拟机运行从（可能不是之前的虚拟机）
 *
 */
.balign ENTRY_SIZE
lower_el_aarch64_sync:
    VM_EXIT
    bl  aborts_sync_handler
    b   vcpu_arch_entry
.balign ENTRY_SIZE
lower_el_aarch64_irq:    
    VM_EXIT
    bl  gic_handle
    b   vcpu_arch_entry
.balign ENTRY_SIZE
lower_el_aarch64_fiq:    
    b   .
.balign ENTRY_SIZE
lower_el_aarch64_serror: 
    b   .          

/* 
 * Lower EL using AArch32（通俗理解就是Guest OS运行在AArch32状态）
 *
 * 因为我们的设计，只考虑AArch64状态，所以AArch32状态暂不考虑
 */  
.balign ENTRY_SIZE   
lower_el_aarch32_sync:   
    b   .
.balign ENTRY_SIZE
lower_el_aarch32_irq:    
    b   .
.balign ENTRY_SIZE
lower_el_aarch32_fiq:    
    b   .
.balign ENTRY_SIZE
lower_el_aarch32_serror: 
    b   .

.balign ENTRY_SIZE